package com.jadows.tdog.message.configuration;

import com.jadows.tdog.message.WsReqWrapper;
import com.jadows.tdog.message.annotations.Action;
import com.jadows.tdog.message.details.AbstractMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.MethodIntrospector;
import org.springframework.lang.NonNull;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author taojiacheng
 */
@Slf4j
@Configuration
public class WSActionDefinitionRegister implements BeanPostProcessor {

    private final Map<String, ActionDefinition> registry = new ConcurrentHashMap<>();


    @Override
    public Object postProcessBeforeInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException {
        MethodIntrospector.selectMethods(bean.getClass(), (MethodIntrospector.MetadataLookup<Boolean>) method -> {
            Action action = method.getAnnotation(Action.class);
            if (action == null) {
                return false;
            }
            Class<?>[] parameterTypes = method.getParameterTypes();
            if (parameterTypes.length != 1) {
                throw new IllegalStateException("@Action method Parameter count must be 1");
            }
            // 获取参数类型
            Class<?> parameterType = method.getParameterTypes()[0];
            if (!WsReqWrapper.class.isAssignableFrom(parameterType)) {
                throw new IllegalStateException("@Action method Parameter type must be WsReqWrapper");
            }

            Type genericParamType = method.getGenericParameterTypes()[0];
            Type genericArgType = null;
            if (genericParamType instanceof ParameterizedType parameterizedType) {
                // 获取泛型参数的类型
                genericArgType = parameterizedType.getActualTypeArguments()[0];
            }
            if (genericArgType != null) {
                if (!AbstractMessage.class.isAssignableFrom((Class<?>) genericArgType)) {
                    throw new IllegalStateException("Generic parameter type must be AbstractMessage");
                }
            }
            for (String actionName : action.value()) {
                register(actionName, bean, method, genericArgType);
            }
            return true;
        });
        return bean;
    }

    protected void register(String actionName, Object bean, Method method, Type genericParameterType) {
        ActionDefinition actionDefinition = new ActionDefinition(bean, method, genericParameterType);
        ActionDefinition old = registry.putIfAbsent(actionName, actionDefinition);
        if (old != null) {
            throw new IllegalStateException("Duplicate action name: " + actionName);
        }
    }

    public ActionDefinition get(String actionName) {
        return registry.get(actionName);
    }

    public record ActionDefinition(Object bean, Method method, Type genericParameterType) {
    }
}
